package com.etoonet.android.updater;

import android.annotation.SuppressLint;
import android.app.DownloadManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.FileProvider;
import android.text.TextUtils;
import android.widget.Toast;

import java.io.File;

public class Updater {

    /**
     * FileDownloadManager.getDownloadStatus如果没找到会返回-1
     */
    private static final int STATUS_UN_FIND = -1;

    public static final long INVALID_DOWNLOAD_ID = -1L;

    private static final String PREF_APP_UPDATER = "pref_app_updater";
    private static final String KEY_DOWNLOAD_ID = "app_download_id";
    private static final String MIME_TYPE_APK = "application/vnd.android.package-archive";
    private static final String DOWNLOADS_PROVIDER = "com.android.providers.downloads";

    private static Updater instance;

    private String mFileProviderAuthority;
    private UpdaterListener mUpdaterListener;

    public interface UpdaterListener {
        void onInstallApk();
    }

    private Updater() {

    }

    public synchronized static Updater get() {
        if (instance == null) {
            instance = new Updater();
        }
        return instance;
    }

    public void showLog(boolean log) {
        Logger.get().setShowLog(log);
    }

    public String getFileProviderAuthority() {
        return mFileProviderAuthority;
    }

    public void setFileProviderAuthority(String fileProviderAuthority) {
        this.mFileProviderAuthority = fileProviderAuthority;
    }

    public long download(UpdaterConfig updaterConfig) {
        showLog(updaterConfig.getShowLog());

        if (!checkDownloadState(updaterConfig.getContext())) {
            Toast.makeText(updaterConfig.getContext(), R.string.system_download_component_disable, Toast.LENGTH_SHORT).show();
            showDownloadSetting(updaterConfig.getContext());
            // 判断是否存在，否则使用浏览器下载
            return INVALID_DOWNLOAD_ID;
        }

        long downloadId = getLocalDownloadId(updaterConfig.getContext());

        Logger.get().d("local download id is " + downloadId);
        if (downloadId != INVALID_DOWNLOAD_ID) {

            // 获取下载状态
            int status = Updater.getDownloadStatus(updaterConfig.getContext(), downloadId);
            switch (status) {
                case DownloadManager.STATUS_SUCCESSFUL: // 下载成功
                    Logger.get().d("downloadId=" + downloadId + " ,status = STATUS_SUCCESSFUL");
                    Uri uri = Updater.getDownloadUri(updaterConfig.getContext(), downloadId);
                    if (uri != null) {
                        // 本地的版本大于当前程序的版本直接安装
                        if (compare(updaterConfig.getContext(), uri)) {
                            Logger.get().d("start install UI");
                            installApk(updaterConfig.getContext(), downloadId);
                            return INVALID_DOWNLOAD_ID;
                        } else {
                            // 从DownloadManager中移除这个任务
                            Updater.getDownloadManager(updaterConfig.getContext()).remove(downloadId);
                        }
                    }
                    // 重新下载
                    downloadId = startDownload(updaterConfig);
                    break;

                case DownloadManager.STATUS_FAILED: // 下载失败
                    Logger.get().d("download failed " + downloadId);
                    downloadId = startDownload(updaterConfig);
                    break;
                case DownloadManager.STATUS_RUNNING:
                    Logger.get().d("downloadId=" + downloadId + " ,status = STATUS_RUNNING");
                    break;
                case DownloadManager.STATUS_PENDING:
                    Logger.get().d("downloadId=" + downloadId + " ,status = STATUS_PENDING");
                    break;
                case DownloadManager.STATUS_PAUSED:
                    Logger.get().d("downloadId=" + downloadId + " ,status = STATUS_PAUSED");
                    break;
                case STATUS_UN_FIND:
                    Logger.get().d("downloadId=" + downloadId + " ,status = STATUS_UN_FIND");
                    downloadId = startDownload(updaterConfig);
                    break;
                default:
                    Logger.get().d("downloadId=" + downloadId + " ,status = " + status);
                    break;
            }
        } else {
            downloadId = startDownload(updaterConfig);
        }
        return downloadId;
    }

    private static DownloadManager getDownloadManager(Context context) {
        return (DownloadManager) context.getApplicationContext().getSystemService(Context.DOWNLOAD_SERVICE);
    }

    private long startDownload(UpdaterConfig updaterConfig) {
        DownloadManager.Request req = new DownloadManager.Request(Uri.parse(updaterConfig.getFileUrl()));
        req.setAllowedNetworkTypes(updaterConfig.getAllowedNetworkTypes());
        // 漫游移动网络是否允许下载
        req.setAllowedOverRoaming(updaterConfig.isAllowedOverRoaming());

        if (updaterConfig.isCanMediaScanner()) {
            // 能够被MediaScanner扫描
            req.allowScanningByMediaScanner();
        }

        // 是否显示状态栏下载UI
        req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
        // 正在下载的Notification进入下载详情界面
        req.setVisibleInDownloadsUi(updaterConfig.isShowDownloadUI());

        req.setDestinationUri(updaterConfig.getDestinationUri());

        req.setTitle(updaterConfig.getTitle());
        req.setDescription(updaterConfig.getDescription());

        long downloadId = getDownloadManager(updaterConfig.getContext()).enqueue(req);
        saveDownloadId(updaterConfig.getContext(), downloadId);
        Logger.get().d("apk download start, downloadId is " + downloadId);
        return downloadId;
    }

    public void setOnInstallApkListener(UpdaterListener onInstallApkListener) {
        mUpdaterListener = onInstallApkListener;
    }

    @Nullable
    public UpdaterListener getOnInstallApkListener() {
        return mUpdaterListener;
    }

    public static long getLocalDownloadId(Context context) {
        SharedPreferences sharedPreferences = context.getSharedPreferences(PREF_APP_UPDATER, Context.MODE_PRIVATE);
        return sharedPreferences.getLong(KEY_DOWNLOAD_ID, -1L);
    }

    @SuppressLint("ApplySharedPref")
    public static void saveDownloadId(Context context, Long id) {
        SharedPreferences sharedPreferences = context.getSharedPreferences(PREF_APP_UPDATER, Context.MODE_PRIVATE);
        sharedPreferences.edit()
                .putLong(KEY_DOWNLOAD_ID, id)
                .commit();
    }

    /**
     * 获取文件保存的路径
     *
     * @param downloadId an ID for the download, unique across the system.
     *                   This ID is used to make future calls related to this download.
     * @return file path
     * @see #getDownloadUri(Context, long)
     */
    private String getDownloadPath(Context context, long downloadId) {
        DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
        Cursor c = getDownloadManager(context).query(query);
        if (c != null) {
            try {
                if (c.moveToFirst()) {
                    return c.getString(c.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI));
                }
            } finally {
                c.close();
            }
        }
        return null;
    }

    /**
     * 获取保存文件的地址
     *
     * @param downloadId an ID for the download, unique across the system.
     *                   This ID is used to make future calls related to this download.
     * @see #getDownloadPath(Context, long)
     */
    public static Uri getDownloadUri(Context context, long downloadId) {
        return getDownloadManager(context).getUriForDownloadedFile(downloadId);
    }

    /**
     * 获取下载状态
     *
     * @param downloadId an ID for the download, unique across the system.
     *                   This ID is used to make future calls related to this download.
     * @return int
     * @see DownloadManager#STATUS_PENDING
     * @see DownloadManager#STATUS_PAUSED
     * @see DownloadManager#STATUS_RUNNING
     * @see DownloadManager#STATUS_SUCCESSFUL
     * @see DownloadManager#STATUS_FAILED
     */
    public static int getDownloadStatus(Context context, long downloadId) {
        DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
        Cursor c = getDownloadManager(context).query(query);
        if (c != null) {
            try {
                if (c.moveToFirst()) {
                    return c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
                }
            } finally {
                c.close();
            }
        }
        return -1;
    }

    public static DownloadInfo getDownloadInfo(Context context, long downloadId) {
        DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
        Cursor c = getDownloadManager(context).query(query);
        if (c != null) {
            try {
                if (c.moveToFirst() && c.getCount() > 0) {
                    long id = c.getLong(c.getColumnIndexOrThrow(DownloadManager.COLUMN_ID));
                    int status = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
                    String uri = c.getString(c.getColumnIndexOrThrow(DownloadManager.COLUMN_URI));
                    String localUri = c.getString(c.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI));
                    String title = c.getString(c.getColumnIndexOrThrow(DownloadManager.COLUMN_TITLE));
                    String desc = c.getString(c.getColumnIndexOrThrow(DownloadManager.COLUMN_DESCRIPTION));
                    String mediaType = c.getString(c.getColumnIndexOrThrow(DownloadManager.COLUMN_MEDIA_TYPE));
                    String reason = c.getString(c.getColumnIndexOrThrow(DownloadManager.COLUMN_REASON));
                    long totalSize = c.getLong(c.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
                    long size = c.getLong(c.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));

                    DownloadInfo downloadInfo = new DownloadInfo();
                    downloadInfo.setId(id);
                    downloadInfo.setStatus(status);
                    downloadInfo.setUri(uri);
                    downloadInfo.setLocalUri(localUri);
                    downloadInfo.setTitle(title);
                    downloadInfo.setDesc(desc);
                    downloadInfo.setMediaType(mediaType);
                    downloadInfo.setReason(reason);
                    downloadInfo.setTotalSize(totalSize);
                    downloadInfo.setSize(size);

                    return downloadInfo;
                } else {
                    return null;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                c.close();
            }
        }
        return null;
    }

    /**
     * 下载的apk和当前程序版本比较
     *
     * @param context Context 当前运行程序的Context
     * @param apkUri  apk uri
     * @return 如果当前应用版本小于apk的版本则返回true
     */
    private boolean compare(Context context, Uri apkUri) {
        String path = AndroidUriUtils.getRealFilePath(context, apkUri);
        PackageInfo apkInfo = context.getPackageManager().getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES);

        String localPackage = context.getPackageName();

        try {
            PackageInfo packageInfo = context.getPackageManager().getPackageInfo(localPackage, 0);
            if (Logger.get().isShowLog()) {
                Logger.get().e("apk file packageName=" + apkInfo.packageName +
                        ", versionName=" + apkInfo.versionName);
                Logger.get().e("current app packageName=" + packageInfo.packageName +
                        ", versionName=" + packageInfo.versionName);
            }
            if (apkInfo.packageName.equals(localPackage)) {
                if (apkInfo.versionCode > packageInfo.versionCode) {
                    return true;
                }
            }
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }

        return false;
    }

    public static void installApk(@NonNull Context context, long downloadApkId) {
        installApk(context, null, downloadApkId);
    }

    public static void installApk(@NonNull Context context, @Nullable String fileAuthority, long downloadApkId) {
        Object service = context.getSystemService(Service.DOWNLOAD_SERVICE);
        if (service == null)
            return;

        DownloadManager dm = (DownloadManager) service;
        Uri downloadFileUri = dm.getUriForDownloadedFile(downloadApkId);

        if (downloadFileUri != null) {
            String fileName = AndroidUriUtils.getRealFilePath(context, downloadFileUri);
            File file = new File(fileName);
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            Uri apkUri;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                if (TextUtils.isEmpty(fileAuthority)) {
                    apkUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", file);
                } else {
                    apkUri = FileProvider.getUriForFile(context, fileAuthority, file);
                }
            } else {
                apkUri = Uri.fromFile(new File(fileName));
            }
            intent.setDataAndType(apkUri, MIME_TYPE_APK);
            context.startActivity(intent);
        }

        UpdaterListener listener = get().getOnInstallApkListener();
        if (listener != null) {
            listener.onInstallApk();
        }
    }

    private static boolean checkDownloadState(@NonNull Context context) {
        try {
            int state = context.getPackageManager().getApplicationEnabledSetting(DOWNLOADS_PROVIDER);
            return state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                    || state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
        } catch (Exception e) {
            return false;
        }
    }

    private static void showDownloadSetting(@NonNull Context context) {
        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        intent.setData(Uri.parse("package:" + DOWNLOADS_PROVIDER));
        if (context.getPackageManager().resolveActivity(intent, 0) != null) {
            context.startActivity(intent);
        }
    }

}
